package edu.cmu.vlis.datamining.utils;

import java.io.File;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.zip.GZIPInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.jgrapht.VertexFactory;
import org.jgrapht.graph.ClassBasedEdgeFactory;
import org.jgrapht.graph.Multigraph;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException;

import edu.cmu.vlis.datamining.core.Edge;
import edu.cmu.vlis.datamining.core.Vertex;



public class ParserBlogBlog {

    Multigraph<Vertex,Edge > graph;
    Map <String,Vertex> vertexMap;
    int uniqueId = 1;
    int numEdges= 0;
    int noLinksCount = 0;
    int badBlogsCount=0;
    BufferedReader reader;
    DocumentBuilder builder;
    Document doc;
    VertexFactory<Vertex> vertexFac = null;

    public ParserBlogBlog() {

        this.vertexMap = new HashMap<String, Vertex>();
        this.graph =     new Multigraph<Vertex,Edge>(new ClassBasedEdgeFactory<Vertex,Edge>(Edge.class));

        //Create a document Parser
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true); 
        try {
            this.builder = domFactory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }

    }

    public void processBlog(String xmlBlog){

        //System.out.println("Starting processing of: "+vertexMap.size());
        try {
            InputSource inStream = new InputSource();
            inStream.setEncoding("UTF-8");
            inStream.setCharacterStream(new StringReader(xmlBlog));
            this.doc = this.builder.parse(inStream);
        }
        catch (MalformedByteSequenceException im) {
            System.out.println("MalformedByteSequenceException");
            badBlogsCount++;
            return;
        }catch (SAXException e) {
            System.out.println("SAXException");
            badBlogsCount++;
            return;
        } catch(IOException e){
            System.out.println("IOException");
            badBlogsCount++;
            return;
        }
        this.doc.getDocumentElement().normalize();

        //Retrieve Blog Url
        String blogUrl = doc.getElementsByTagName("weblog_url").item(0).getChildNodes().item(0).getNodeValue().trim();

        //Retrieve Blog Title
        NodeList nodeList = doc.getElementsByTagName("weblog_title").item(0).getChildNodes();
        String webLogTitle ="";
        if(nodeList.getLength()!=0) 
            webLogTitle = nodeList.item(0).getNodeValue().trim();

        //Retrieve BlogPost Title 
        nodeList = doc.getElementsByTagName("post_title").item(0).getChildNodes();
        String blogPostTitle = "";
        if(nodeList.getLength()!=0) 
            blogPostTitle = nodeList.item(0).getNodeValue().trim();


        //Retrieve Blog AuthorName
        nodeList = doc.getElementsByTagName("author_name").item(0).getChildNodes();
        String authorName="";
        if(nodeList.getLength()!=0) 
            authorName = nodeList.item(0).getNodeValue().trim();

        //Retrieve Blog Date Posted
        nodeList = doc.getElementsByTagName("date_posted").item(0).getChildNodes();
        String dateStr="";
        Date blogDate = null;
        if(nodeList.getLength()!=0)  {
            dateStr = nodeList.item(0).getNodeValue().trim();

            DateFormat dateFormat = new SimpleDateFormat("yyyymmdd");

            try {
                blogDate = dateFormat.parse(dateStr);
            } catch (ParseException e) {
                System.err.println("Invalid Date Parser Exception ");
                e.printStackTrace();
            }
        }

        //Get the outlinks of the blog
        nodeList = doc.getElementsByTagName("outlink");

        if(nodeList.getLength()==0)
            this.noLinksCount ++;

        else{

            //create Vertex for this blog
            Vertex v;
            if(vertexMap.containsKey(blogUrl)) 
                v = vertexMap.get(blogUrl);

            else {
                uniqueId++;
                v = new Vertex(uniqueId,blogUrl);
                vertexMap.put(blogUrl, v);
                //Set all attributes for the vertex
                v.setBlogPostTitle(blogPostTitle);
                v.setWebLogTitle(webLogTitle);
                v.setAuthorName(authorName);
                v.setDatePosted(blogDate);
                graph.addVertex(v);
            }   

            //Create the edges

            numEdges += nodeList.getLength();
            for(int i=0;i<nodeList.getLength();i++) {
                Node outLinkNode = nodeList.item(i);
                if(outLinkNode.getNodeType() == Node.ELEMENT_NODE){
                    Element el = (Element)outLinkNode;

                    String outUrl = el.getElementsByTagName("url").item(0).getChildNodes().item(0).getNodeValue().trim();
                    String outSite =  el.getElementsByTagName("site").item(0).getChildNodes().item(0).getNodeValue().trim();
                    String outType = el.getElementsByTagName("type").item(0).getChildNodes().item(0).getNodeValue().trim();

                    //Check if url vertex already exists in the Else Create it
                    Vertex vOut =null;
                    /*if(outSite=="http://community.livejournal.com/gopota_ru/?skip=340" ) {
                    	System.out.println("whts wrong");
                    }

                    if(outUrl=="http://community.livejournal.com/gopota_ru/?skip=340" ) {
                    	System.out.println("Thts the error");
                    }*/
                    if(vertexMap.containsKey(outSite)) {
                        vOut = vertexMap.get(outSite);
                        if(vOut.getVertexId() == v.getVertexId())
                            continue;
                    }else {
                        uniqueId++;
                        vOut = new Vertex(uniqueId,outSite);
                        vertexMap.put(outSite,vOut);
                        vOut.setType(outType);
                        graph.addVertex(vOut);
                    }   
                    //                  //Set all attributes for the vertex
                    //                  //vOut.setSite(outUrl);
                    //                  if(wg.containsVertex(vOut))
                    //                      System.out.println("Cotains Vout");
                    //                  if(wg.containsVertex(v))
                    //                      System.out.println("Cotains V");

                    graph.addEdge(v, vOut);
                }
            }
        }
    }

    public void processGzippedFile(File file) {
        try {
            this.reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file))));
            String line;
            boolean flag_on=false;
            String xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
            String xmlBlog = xmlHeader;
            while ((line = reader.readLine()) != null) {
                if (line.contains("<post>")) {  //start building the message
                    flag_on = true;
                }
                if(flag_on)
                    xmlBlog = xmlBlog.concat(line);
                if(line.contains("</post>")) {
                    //process the XMl message as it is one blog
                    processBlog(xmlBlog);
                    xmlBlog = xmlHeader;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }

    public void printStats() {

        System.out.println("Total number of Vertices/blogs in graph " + vertexMap.size());
        System.out.println("Totalnumber of Edges/connections  in the graph " + this.numEdges);
        System.out.println("Number of blogs not having any outlinks  " + this.noLinksCount);
        System.out.println("Number of blogs not processed  " + this.badBlogsCount);
    }

    public void parse(File dir) {

        //Read the files in directory data
        File[] gzippedFiles = dir.listFiles();

        for(File gzippedFile : gzippedFiles){

            System.out.println("Starting processing of "+gzippedFile.getAbsolutePath());
            this.processGzippedFile(gzippedFile);
            printStats();
        }
        System.out.println("Done processing all gzipped files.");
    }

    /**
     * @return the graph
     */
    public Multigraph<Vertex, Edge> getGraph() {
        return this.graph;
    }

    /**
     * @return the vertexMap
     */
    public Map<String, Vertex> getVertexMap() {
        return vertexMap;
    }
}
